In [1]:
import pandas as pd
import os
import numpy as np
import matplotlib.pyplot as plt
import altair as alt
import altair_saver 

Streamflow¶

Below you will graphs, maps, and images describing streamflow conditions in the water supply areas during the 2023-24 winter season.

In [2]:
os.chdir('C://Users/pmarshal/Documents/Climate-Outlook/monthly-climate')
os.getcwd()
Out[2]:
'C:\\Users\\pmarshal\\Documents\\Climate-Outlook\\monthly-climate'
In [3]:
# Import dataset
cap_flow = pd.read_csv('data/cap_flows.csv', parse_dates=['date']) 
cap_flow['year'] = pd.DatetimeIndex(cap_flow['date']).year
cap_flow['month'] = pd.DatetimeIndex(cap_flow['date']).month
cap_flow['day'] = pd.DatetimeIndex(cap_flow['date']).day
cap_flow['DOY'] = pd.DatetimeIndex(cap_flow['date']).dayofyear
In [4]:
# Import dataset
cap_hourly = pd.read_csv('data/cap_lakehead_hourly.csv', parse_dates=['datetime']) 
cap_hourly.head()
Out[4]:
datetime flow return_2 return_5
0 2023-01-01 00:00:00 28.5 358 468
1 2023-01-01 01:00:00 27.6 358 468
2 2023-01-01 02:00:00 26.7 358 468
3 2023-01-01 03:00:00 25.9 358 468
4 2023-01-01 04:00:00 25.2 358 468
In [27]:
alt.data_transformers.disable_max_rows()

cap_hour_2023 = alt.Chart(cap_hourly).mark_line(color= "darkblue", size= 2, interpolate='basis').encode(
    alt.X('monthdate(datetime):T', title=None),
    alt.Y('flow:Q', title='Inflow (m^3/s)', scale=alt.Scale(domain=[0, 500])),
    tooltip=["datetime:T", "flow"],
)

return_2 = alt.Chart(cap_hourly).mark_line(color='darkgreen', opacity=0.6, strokeDash=[4, 2], size= 1).encode(
    alt.X('monthdate(datetime):T', title=None),
    alt.Y('return_2:Q', title='Inflow (m^3/s)')
)
return_5 = alt.Chart(cap_hourly).mark_line(color='darkred', opacity=0.6, strokeDash=[4, 2], size= 1).encode(
    alt.X('monthdate(datetime):T', title=None),
    alt.Y('return_5:Q', title='Inflow (m^3/s)')
)
total_hourly = cap_hour_2023 + return_2 + return_5.properties(width=600)
total_hourly
Out[27]:
In [28]:
# Claculate statistics on mean temp column
cap_flow['mean'] = cap_flow.groupby('DOY')['flow'].transform('mean')
cap_flow['max'] = cap_flow.groupby('DOY')['flow'].transform('max')
cap_flow['min'] = cap_flow.groupby('DOY')['flow'].transform('min')
cap_flow['std'] = cap_flow.groupby('DOY')['flow'].transform('std')
cap_flow['sem'] = cap_flow.groupby('DOY')['flow'].transform('sem')
cap_flow['ci95_hi'] = cap_flow['mean'] + 1.96* cap_flow['sem']
cap_flow['ci95_lo'] = cap_flow['mean'] - 1.96* cap_flow['sem']
In [29]:
# Create dataframe with data for the current year
cap_current_year = cap_flow.loc[cap_flow['year'] == 2024]
# Create dataframe with historical data (i.e. not == current year)
cap_past = cap_flow.loc[cap_flow['year'] != 2024]
cap_stats = cap_past.loc[0:364]

Mean Daily Inflows¶

In [30]:
alt.data_transformers.disable_max_rows()

cap_rule1 = alt.Chart(pd.DataFrame({
  'flow': ['240'],
  'color': ['gray']
})).mark_rule(opacity=0.8, strokeDash=[3,3]).encode(
  y='flow:Q',
  color=alt.Color('color:N', scale=None)
)

cap_rule2 = alt.Chart(pd.DataFrame({
  'flow': ['295'],
  'color': ['darkblue']
})).mark_rule(opacity=0.8, strokeDash=[3,3]).encode(
  y='flow:Q',
  color=alt.Color('color:N', scale=None)
)

cap_title = alt.TitleParams(
   text='Capilano River above Lakehead (08GA010)',
   subtitle="Mean Daily Inflows",
   anchor='middle',
   fontSize=14,
   fontWeight='bold')

cap_2023 = alt.Chart(cap_flow[cap_flow['year'] == 2023], title=cap_title).mark_line(color= "darkblue", size= 2).encode(
    alt.X('monthdate(date):T', title=None),
    alt.Y('flow:Q', title='Inflow (m^3/s)', scale=alt.Scale(domain=[0, 300])),
    tooltip=["date:T", "flow"],
)

cap_mean = alt.Chart(cap_past).mark_line(color='black', opacity=0.6, strokeDash=[4, 2], size= 1).encode(
    alt.X('monthdate(date):T', title=None),
    alt.Y('mean(flow):Q', title='Inflow (m^3/s)')
)

cap_area2 = alt.Chart(cap_stats).mark_area(color='#919397', opacity=0.6).encode(
    alt.X('monthdate(date)'),
    alt.Y('ci95_hi:Q'),
    alt.Y2('ci95_lo:Q')
)
In [32]:
# Configure the x-axis of the combined chart to display only the first of the month
cap_combined_chart = (cap_area2 + cap_mean + cap_2023 + cap_rule1 + cap_rule2).properties(width=600)
cap_combined_chart
Out[32]:
{admonition}
:class: attention
You can find realtime inflow data for Capilano River above Lakehead on the [Water Survey Canada Hydrometric Data website](https://wateroffice.ec.gc.ca/report/real_time_e.html?stn=08GA010)
In [33]:
# Import dataset
seyLH_flow = pd.read_csv('data/seyLH_flow.csv', parse_dates=['date']) 
seyLH_flow['year'] = pd.DatetimeIndex(seyLH_flow['date']).year
seyLH_flow['month'] = pd.DatetimeIndex(seyLH_flow['date']).month
seyLH_flow['day'] = pd.DatetimeIndex(seyLH_flow['date']).day
seyLH_flow['DOY'] = pd.DatetimeIndex(seyLH_flow['date']).dayofyear
In [34]:
# Claculate statistics on mean temp column
seyLH_flow['mean'] = seyLH_flow.groupby('DOY')['flow'].transform('mean')
seyLH_flow['max'] = seyLH_flow.groupby('DOY')['flow'].transform('max')
seyLH_flow['min'] = seyLH_flow.groupby('DOY')['flow'].transform('min')
seyLH_flow['std'] = seyLH_flow.groupby('DOY')['flow'].transform('std')
seyLH_flow['sem'] = seyLH_flow.groupby('DOY')['flow'].transform('sem')
seyLH_flow['ci95_hi'] = seyLH_flow['mean'] + 1.96* seyLH_flow['sem']
seyLH_flow['ci95_lo'] = seyLH_flow['mean'] - 1.96* seyLH_flow['sem']
In [35]:
# Create dataframe with data for the current year
sey_current_year = seyLH_flow.loc[seyLH_flow['year'] == 2024]
# Create dataframe with historical data (i.e. not == current year)
sey_past = seyLH_flow.loc[seyLH_flow['year'] != 2024]
sey_stats = sey_past.loc[92:456]
In [36]:
alt.data_transformers.disable_max_rows()

sey_rule1 = alt.Chart(pd.DataFrame({
  'flow': ['240'],
  'color': ['gray']
})).mark_rule(opacity=0.8, strokeDash=[3,3]).encode(
  y='flow:Q',
  color=alt.Color('color:N', scale=None)
)

sey_rule2 = alt.Chart(pd.DataFrame({
  'flow': ['295'],
  'color': ['darkblue']
})).mark_rule(opacity=0.8, strokeDash=[3,3]).encode(
  y='flow:Q',
  color=alt.Color('color:N', scale=None)
)

sey_title = alt.TitleParams(
   text='Seymour River above Lakehead (08GA079)',
   subtitle="Mean Daily Inflows",
   anchor='middle',
   fontSize=14,
   fontWeight='bold')

sey_2023 = alt.Chart(seyLH_flow[seyLH_flow['year'] == 2023], title=sey_title).mark_line(color= "darkblue", size= 2).encode(
    alt.X('monthdate(date):T', title=None),
    alt.Y('flow:Q', title='Inflow (m^3/s)', scale=alt.Scale(domain=[0, 100])),
    tooltip=["year:N", "flow"],
)

sey_mean = alt.Chart(sey_past).mark_line(color='black', opacity=0.6, strokeDash=[4, 2], size= 1).encode(
    alt.X('monthdate(date):T', title=None),
    alt.Y('mean(flow):Q', title='Inflow (m^3/s)')
)

sey_area2 = alt.Chart(sey_stats).mark_area(color='#919397', opacity=0.6).encode(
    alt.X('monthdate(date)'),
    alt.Y('ci95_hi:Q'),
    alt.Y2('ci95_lo:Q')
)
In [37]:
# Configure the x-axis of the combined chart to display only the first of the month
sey_combined_chart = (sey_area2 + sey_mean + sey_2023 + sey_rule1 + sey_rule2).properties(width=600)
sey_combined_chart
Out[37]:
{admonition}
:class: attention
You can find realtime inflow data for Seymour River above Lakehead on the [Water Survey Canada Hydrometric Data website](https://wateroffice.ec.gc.ca/report/real_time_e.html?stn=08GA079)
In [38]:
cap_flow2 = pd.read_csv('data/cap_flows.csv')
cap_flow2['date'] = pd.to_datetime(cap_flow2['date'])
cap_flow2['year'] = pd.DatetimeIndex(cap_flow2['date']).year
cap_flow2['month'] = pd.DatetimeIndex(cap_flow2['date']).month
cap_flow2.set_index('date', inplace=True)
cap_monthly_mean = cap_flow2.resample('M').mean()
cap_monthly_mean.reset_index(inplace=True)
cap_monthly_mean['year'] = pd.DatetimeIndex(cap_monthly_mean['date']).year
cap_monthly_mean.head()
Out[38]:
date flow year month
0 1998-01-31 34.439032 1998 1.0
1 1998-02-28 32.126071 1998 2.0
2 1998-03-31 18.635484 1998 3.0
3 1998-04-30 11.063333 1998 4.0
4 1998-05-31 24.777419 1998 5.0
In [39]:
#qd_monthly['date'] = pd.to_datetime(qd_monthly[['year', 'month']].assign(day=1))
cap_monthly_mean['mean_flow'] = cap_monthly_mean.groupby('month')['flow'].transform('mean').round(1)
cap_monthly_mean['percent'] = cap_monthly_mean['flow']/cap_monthly_mean['mean_flow'].round(1)
cap_monthly_mean.head()
Out[39]:
date flow year month mean_flow percent
0 1998-01-31 34.439032 1998 1.0 28.9 1.191662
1 1998-02-28 32.126071 1998 2.0 15.7 2.046247
2 1998-03-31 18.635484 1998 3.0 20.9 0.891650
3 1998-04-30 11.063333 1998 4.0 22.4 0.493899
4 1998-05-31 24.777419 1998 5.0 27.1 0.914296
In [40]:
yr_2023 = cap_monthly_mean.loc[cap_monthly_mean['year'] == 2023]
yr_2023.to_csv('yr_2023_flow.csv', index=False)

Monthly Average Inflows and Statistics¶

In [41]:
# Filter the data for the current year (2023)
#data_2023 = qd_monthly[qd_monthly['year'] == 2023]

data_all = cap_monthly_mean[cap_monthly_mean['year'] != 2023]


title2 = alt.TitleParams(
   text='Capilano River above Lakehead (08GA010)',
   subtitle="Average Monthly Inflows",
   anchor='middle',
   fontSize=14,
   fontWeight='bold')

cap_monthly_2023 = alt.Chart(yr_2023).mark_tick(size=14, thickness=3, color='green').encode(
    alt.X('month(date):O'),
    alt.Y('flow:Q'),
    tooltip=[alt.Tooltip('date', title='Date'), alt.Tooltip('flow', title='Inflow')]
).properties(width=400, height=250)

cap_boxplot = alt.Chart(data_all).mark_boxplot(opacity=0.7).encode(
    alt.X('month(date):O', title=' '),
    alt.Y('flow:Q', title='River Inflow (m^3/s)')
).properties(width=400, height=250)
cap_plots = cap_boxplot + cap_monthly_2023
cap_plots
Out[41]:
In [42]:
# Create a chart for 'TA_Anomaly'
flow_anomaly_chart = alt.Chart(yr_2023).mark_bar(color='darkgreen').encode(
    x=alt.X('percent:Q', title=None, axis=alt.Axis(format='%')),
    y=alt.Y('monthdate(date):O', title=None, axis=alt.Axis(format='%b')),
    tooltip=[alt.Tooltip('date', title='Date'), alt.Tooltip('percent', title='Percent')]
).properties(
    title = 'Percent of Normal',width=100, height=250
)

rule1 = alt.Chart(pd.DataFrame({
  'percent': ['1'],
  'color': ['gray']
})).mark_rule(opacity=0.8, strokeDash=[3,3]).encode(
  x='percent:Q',
  color=alt.Color('color:N', scale=None)
)

text_percent = flow_anomaly_chart.mark_text(
    align='left',
    baseline='middle',
    size=10,
    dx=3  # Nudges text to right so it doesn't appear on top of the bar
).encode(
    text=alt.Text('percent:Q', format='.0%'),  # Format as percentage
)
anomaly_text = flow_anomaly_chart + text_percent

anomaly = anomaly_text + rule1
month_flow = cap_plots | anomaly
month_flow
Out[42]:
In [43]:
sey_flow2 = pd.read_csv('data/seyLH_flow.csv')
sey_flow2['date'] = pd.to_datetime(sey_flow2['date'])
sey_flow2.set_index('date', inplace=True)
sey_monthly_mean = sey_flow2.resample('M').mean()
sey_monthly_mean.reset_index(inplace=True)
sey_monthly_mean['year'] = pd.DatetimeIndex(sey_monthly_mean['date']).year
In [44]:
sey_yr_2023 = sey_monthly_mean.loc[sey_monthly_mean['year'] == 2023]
In [45]:
title3 = alt.TitleParams(
   text='Seymour River above Lakehead (08GA010)',
   subtitle="Average Monthly Inflows",
   anchor='middle',
   fontSize=14,
   fontWeight='bold')

sey_monthly_2023 = alt.Chart(sey_yr_2023).mark_tick(size=14, thickness=3, color='green').encode(
    alt.X('month(date):O'),
    alt.Y('flow:Q'),
    tooltip=[alt.Tooltip('date', title='Date'), alt.Tooltip('flow', title='Inflow')]
)

sey_boxplot = alt.Chart(sey_monthly_mean, title=title3).mark_boxplot(opacity=0.7).encode(
    alt.X('month(date):O', title=' '),
    alt.Y('flow:Q', title='River Inflow (m^3/s)')
)
sey_plots = sey_boxplot + sey_monthly_2023
sey_plots.properties(width=600, height=200)
Out[45]:
{note}
Backwatering occurs at the Seymour Lakehead site in the spring (May and June) when the reservoir is full. This causes elevated water levels and inaccurate inflow readings. 
In [46]:
vol = pd.read_csv('data/cap_volume.csv', parse_dates=['date']) 
vol['DOY'] = pd.DatetimeIndex(vol['date']).dayofyear
vol['month'] = pd.DatetimeIndex(vol['date']).month
vol['year'] = pd.DatetimeIndex(vol['date']).year
vol['tick_date'] = pd.to_datetime(vol['DOY'], format='%j').dt.strftime('%b-%d')
vol['mean'] = vol.groupby('wtr_day')['volume'].transform('mean').round(2)
vol['max'] = vol.groupby('wtr_day')['volume'].transform('max').round(2)
vol['min'] = vol.groupby('wtr_day')['volume'].transform('min').round(2)
vol['std'] = vol.groupby('wtr_day')['volume'].transform('std').round(2)
vol['sem'] = vol.groupby('wtr_day')['volume'].transform('sem').round(2)
vol['ci95_hi'] = vol['mean'] + 1.96* vol['sem']
vol['ci95_lo'] = vol['mean'] - 1.96* vol['sem']
vol['ci95_hi'] = vol['ci95_hi'].round(2)
vol['ci95_lo'] = vol['ci95_lo'].round(2)
In [47]:
# Assuming vol is your DataFrame
vol['percentile_5th'] = vol.groupby('wtr_day')['volume'].transform(lambda x: np.percentile(x, 5))
vol['percentile_95th'] = vol.groupby('wtr_day')['volume'].transform(lambda x: np.percentile(x, 95))
In [48]:
rank = vol.groupby(['wtr_year'])['volume'].sum().rank()
vol.head()
Out[48]:
date wtr_year wtr_day volume DOY month year tick_date mean max min std sem ci95_hi ci95_lo percentile_5th percentile_95th
0 2002-10-01 2003 1 0.1 274 10 2002 Oct-01 1.38 9.7 0.1 2.35 0.50 2.36 0.40 0.20 5.195
1 2002-10-02 2003 2 0.3 275 10 2002 Oct-02 2.44 13.0 0.3 3.74 0.80 4.01 0.87 0.31 11.040
2 2002-10-03 2003 3 0.4 276 10 2002 Oct-03 3.53 14.8 0.4 4.38 0.93 5.35 1.71 0.42 13.625
3 2002-10-04 2003 4 0.6 277 10 2002 Oct-04 4.66 16.0 0.5 5.28 1.13 6.87 2.45 0.62 15.595
4 2002-10-05 2003 5 0.7 278 10 2002 Oct-05 5.54 18.7 0.6 6.04 1.29 8.07 3.01 0.73 17.080

Cumulative River Inflows¶

In [52]:
alt.data_transformers.disable_max_rows()

vert_line = alt.Chart(pd.DataFrame({
  'wtr_day': [61],
  'color': ['black']
})).mark_rule(opacity=0.6, strokeDash=[3,3]).encode(
  x='wtr_day',
  color=alt.Color('color:N', scale=None)
)

# Add text annotation
text1 = alt.Chart().mark_text(
    text='Nov. 30',  # Replace with your desired text
    align='left',
    size=10,
    baseline='middle',
    color='black',
    dx=94  # adjust the position of the text
).encode(
    x=alt.value(10),  # Adjust the x-position of the text box
    y=alt.value(10)   # Adjust the y-position of the text box
)
# Create title
title5 = alt.TitleParams(
   text='Capilano River at Lakehead (08GA010)',
   subtitle="Cumulative inflow during the water year",
   anchor='middle',
   fontSize=14,
   fontWeight='bold')
# Create a selection for the year
#year_selection = alt.selection_single(
 #   name='Select',
 ##   fields=['year'],
#    bind=alt.binding_select(options=list(vol['year'].unique()), name='Select Year '),
#    init={'year': 2023}  # Initial selected year
#)
#scale=alt.Scale(domain=[0,365], nice=False)
# Create a line chart using Altair
#chart6 = alt.Chart(vol).mark_line().encode(
#    alt.X('DOY'),
#    alt.Y('volume:Q', title='Cumulative Inflow (billion litres)'),
#    color=alt.condition(year_selection, 'year:N', alt.value('gray'), title='Year'),
#    strokeWidth=alt.condition(year_selection, alt.value(2.5), alt.value(1)),
#    opacity=alt.condition(year_selection, alt.value(1.0), alt.value(0.6)),
#    tooltip=alt.condition(year_selection, "volume", alt.value(''))
#).add_selection(
#    year_selection
#).properties(
#    width=600
#)
#chart6 
In [ ]:
 
In [ ]: